package com.tealer.mycustomerecylerview.view;

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;

import androidx.annotation.Nullable;
import androidx.core.view.NestedScrollingChild2;
import androidx.core.view.NestedScrollingChildHelper;
import androidx.core.view.ViewCompat;

import com.tealer.mycustomerecylerview.R;

import java.util.ArrayList;
import java.util.List;

/**
 * 描述
 * 手写RecyclerView
 *
 * @author Created by lipengbo
 * @email 1162947801@qq.com
 * @time Created on 2022/11/24 6:46
 */
public class MyRecyclerView extends ViewGroup implements NestedScrollingChild2 {


    /**
     * 当前RecylerView适配器
     */
    private Adapter mAdapter;

    /**
     * 当前显示的View集合
     */
    private List<View> viewlist;

    //当前滑动的Y值
    private int currentY;

    //总行数
    private int rowCount;

    //显示第一行是占内容的第几行（数据源中的position）
    private int firstRow;

    //y偏移量
    private int scrollY;

    //初始化 是否是第一屏(第一屏幕 需要重新绘制冲 重新布局，其他屏幕不需要绘制和布局)
    private boolean needRelayout;

    //当前RecylerView的宽度
    private int width;
    //当前RecylerView的高度
    private int height;

    //所有ItemView的高度数组
    private int[] heights;

    //最小滑动距离（超过最小滑动距离才会执行滑动操作，否则不执行）
    private int touchSlop;

    //回收池
    private Recyler recyler;
    //获取到嵌套滑动子控件的help类
    private NestedScrollingChildHelper nestedScrollingChildHelper;


    public MyRecyclerView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }

    /**
     * 初始化RecyclerView方法
     *
     * @param context
     * @param attrs
     */
    private void init(Context context, AttributeSet attrs) {
        //测量工具类
        ViewConfiguration viewConfiguration = ViewConfiguration.get(context);
        //获取到最小的滑动距离
        touchSlop = viewConfiguration.getScaledTouchSlop();
        //初始化ViewList集合
        viewlist = new ArrayList<>();
        //是否需要重新布局
        needRelayout = true;
        //初始化帮助类
        nestedScrollingChildHelper=new NestedScrollingChildHelper(this);
        //指定嵌套滑动是要使用的
        nestedScrollingChildHelper.setNestedScrollingEnabled(true);


    }

    /**
     * 获取到数组中数据的高度
     *
     * @param heights 数组
     * @param index   从哪一个Item获取
     * @param count   一共要拿多少个Item高度
     * @return
     */
    private int sumArray(int[] heights, int index, int count) {
        int sum = 0;
        count = index + count;
        for (int x = index; x < count; x++) {
            sum += heights[x];
        }

        return sum;
    }


    /**
     * 重写onInterceptTouchEvent方法，来决定滑动还是不滑动
     * @param ev
     * @return
     */
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        boolean intercept=false;
        switch (ev.getAction()){
            case MotionEvent.ACTION_DOWN:
                currentY=(int)ev.getRawY();
                break;
            case MotionEvent.ACTION_MOVE:
                //取出手指当前移动到的Y轴值，跟之前记录的手指触摸位置y相减求差值。
                float y2=Math.abs(currentY-ev.getRawY());
                //分两种情况
                /**
                 * 1、小于最小滑动距离 就不滑动
                 * 2、如果大于最小滑动距离 就滑动
                 */
                if(y2>touchSlop){
                    intercept=true;
                }
                break;
        }
        return intercept;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_MOVE:
                //先获取到当前Y轴的值
                float y2=event.getRawY();
                //手指触摸减去滑动到的这个Y轴的值
                float diff=currentY-y2;
                //不加会影响反应速度
                currentY=(int)y2;
                //调用滑动方法
                scrollBy(0,(int)diff);
                startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL,ViewCompat.TYPE_TOUCH);
                break;
        }
        return super.onTouchEvent(event);
    }

    @Override
    public void scrollBy(int x, int y) {
        scrollY+=y;
        //处理滑动边界问题，纠正scrollY
        scrollY=scrollBounds(scrollY);
        if(scrollY>0){//向上滑
            /**
             * 上滑需要做两件事情
             * 1、将屏幕上方的ItemView移除
             * 2、给屏幕下方添加一个新的ItemView进来
             */
            //1、将屏幕上方的ItemView移除
            while (scrollY>heights[firstRow]){
                //删除第一行，就是删除当前布局中的第0个Item
                removeView(viewlist.remove(0));
                //改变scrolly 要减去这一行的高度
                scrollY-=heights[firstRow];
                //当前显示的行标
                firstRow++;
            }
            //2、给屏幕下方添加一个新的ItemView进来
            //判断当前所显示的View的高度是不是小于RecylerView的高度，如果下于，就添加新的Item
            while (getFillHeight()<height){
                //首先 当前第一行的坐标，加上当前屏幕显示的ItemView的长度，其实就是要添加进去那一行的Item
                 int addLast=firstRow+viewlist.size();
                 //获取到ItemView
                View view=obtainView(addLast,width,heights[addLast]);
                //将新的ItemView添加进viewList
                viewlist.add(view);
            }


        }else if(scrollY<0){//向下滑
            /**
             * 下滑过程中要做两件事情
             * 1、创建一个心的ItemView放置在屏幕上方
             * 2、把最下面移出屏幕的ItemViewy移除掉
             */
            while (scrollY<0){
                //当前显示的ItemView的第一行的行标减去1
                int firstAndRow=firstRow-1;
                //获取到显示在第一行的ItemView的上一个ItemView
                View view=obtainView(firstAndRow,width,heights[firstAndRow]);
                //添加到可见的Item的最上面
                viewlist.add(0,view);
                //更新当前显示的第一行的行标
                firstRow--;
                //改变scrollY，scrollY要将当前添加进去的行的行高加起来
                scrollY+=heights[firstAndRow];
            }
            //把最下面移除了屏幕的item移除掉，判断当前显示的View的总高度是不是大于RecylerView的高度。如果大于将最下面的ItemView移除掉
            while (sumArray(heights,firstRow,viewlist.size())-scrollY-heights[firstRow+viewlist.size()-1]>=height){
                //移除当前显示的在最下面的ItemView
                removeView(viewlist.remove(viewlist.size()-1));
            }

        }else{

        }
        //重新摆放位置
        rePositionView();
    }

    /**
     * 纠正scrollY值
     * @param scrollY
     * @return
     */
    private int scrollBounds(int scrollY) {
        if(scrollY>0){
            //判断上滑的极限  防止滚动的距离  大于当前所有内容的高度
            scrollY=Math.min(scrollY,sumArray(heights,firstRow,heights.length-firstRow)-height);
        }else{
            //判断下滑的极限值，防止滚动的距离 小于第0个Item的高度
            scrollY=Math.max(scrollY,-sumArray(heights,0,firstRow));
        }
         return scrollY;
    }

    /**
     * 当我们上拉或者下滑的时候，重新摆放View的位置
     */
    private void rePositionView() {
        int top=0,right,bottom,left=0,i;
        top-=scrollY;
        //将当前第一行的行标赋值给i
        i=firstRow;
        for (View view:viewlist){
            //重新摆放View
            bottom=top+heights[i++];
            view.layout(0,top,width,bottom);
            top=bottom;
        }


    }

    /**
     * 获取到显示在控件中View的总高度
     * @return
     */
    private int getFillHeight() {
     return sumArray(heights,firstRow,viewlist.size())-scrollY;

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //获取RecylerView在当前布局的宽高
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        //当前内容的高度
        int h = 0;

        //判断适配器是否为空
        if (mAdapter != null) {
            //获取到当前数据中的总条数
            rowCount = mAdapter.getCount();
            //根据适配器的数据的总长度来创建数组
            heights = new int[rowCount];
            //循环获取到所有View的高度
            for (int x = 0; x < heights.length; x++) {
                heights[x] = mAdapter.getHeight(x);
            }
        }
        //获取所有数据的高度
        int tempHeight = sumArray(heights, 0, heights.length);
        //判断所有ItemView的高度和RecylerView的高度谁低
        h = Math.min(tempHeight, heightSize);
        setMeasuredDimension(widthSize, h);
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        //先判断是否需要布局，布局发生改变的时候重新调用
        if (needRelayout || changed) {
            needRelayout = false;
            //清除掉所有的View
            removeAllViews();
            //清除掉当前屏幕中显示的ItemView
            viewlist.clear();
            //如果适配器不为空，就去摆放itemView
            if (mAdapter != null) {
                //获取到RecylerView的宽高
                width = r - l;
                height = b - t;
                //定义布局ItemView的四个变量
                int top = 0, right, bottom, left = 0;
                for (int x = 0; x < rowCount; x++) {
                    //获取到绘制宽度的最右边
                    right = width;
                    bottom = top + heights[x];
                    //生成View
                    View view = makeAndStep(x, left, top, right, bottom);
                    //添加到当前ItemView的集合中
                    viewlist.add(view);
                    //因为循环摆放，所以 下一个控件的top就是上一个控件的bottom，而且需要累加
                    top=bottom;

                }

            }
        }

    }

    @Override
    public void removeView(View view) {
        super.removeView(view);
        int key=(int)view.getTag(R.id.tag_type_view);
        //将View添加进回收池中
        recyler.put(view,key);
    }

    /**
     * 创建View的方法
     *
     * @param x
     * @param left
     * @param top
     * @param right
     * @param bottom
     * @return
     */
    private View makeAndStep(int x, int left, int top, int right, int bottom) {
        //生成View
        View view = obtainView(x, right - left, bottom - top);
        //布局ItemView
        view.layout(left, top, right, bottom);
        return view;
    }

    /**
     * 获取ItemView
     *
     * @param row
     * @param width
     * @param height
     * @return
     */
    public View obtainView(int row, int width, int height) {

        //首先获取到这一行数的Item的布局类型
        int itemViewType = mAdapter.getItemViewType(row);
        //去栈中拿
        View view = recyler.get(itemViewType);
        //定义一个View(用于栈中拿不出来View的时候处理，需要自己生成View)
        View itemView = null;
        if (null == view) {
            itemView = mAdapter.onCreateViewHolder(row, itemView, this);
            if (null == itemView) {
                throw new RuntimeException("onCreateViewHolder 必须要填充布局");
            }
        } else {
            itemView = mAdapter.onBinderViewHolder(row, view, this);
        }
        //给每个ItemView设置一个Tag
        itemView.setTag(R.id.tag_type_view, itemViewType);
        //先测量去每一个ItemView
        itemView.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
                MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
        //每生成一个ItemView都添加进入RecyclerView
        addView(itemView);
        return itemView;
    }

    public Adapter getAdapter() {
        return mAdapter;
    }

    public void setAdapter(Adapter mAdapter) {
        this.mAdapter = mAdapter;
        //初始化回收池
        if(null!=mAdapter){
            recyler=new Recyler(mAdapter.getViewTypeCount());
            scrollY=0;
            firstRow=0;
            needRelayout=true;
            //重新测量，重新摆放
            requestLayout();
        }
    }

    @Override
    public boolean startNestedScroll(int axes, int type) {
        return nestedScrollingChildHelper.startNestedScroll(axes,type);
    }

    @Override
    public void stopNestedScroll(int type) {

    }

    @Override
    public boolean hasNestedScrollingParent(int type) {
        return false;
    }

    @Override
    public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, @Nullable int[] offsetInWindow, int type) {
        return false;
    }

    @Override
    public boolean dispatchNestedPreScroll(int dx, int dy, @Nullable int[] consumed, @Nullable int[] offsetInWindow, int type) {
        return  nestedScrollingChildHelper.dispatchNestedPreScroll(dx,dy,consumed,offsetInWindow,type);
    }

    @Override
    public void stopNestedScroll() {
        super.stopNestedScroll();
        nestedScrollingChildHelper.stopNestedScroll();
    }

    public interface Adapter {

        /**
         * 创建ViewHolder的方法
         *
         * @param position
         * @param convertView
         * @param parent
         * @return
         */
        View onCreateViewHolder(int position, View convertView, ViewGroup parent);

        /**
         * 绑定ViewHolder的方法
         *
         * @param position
         * @param convertView
         * @param parent
         * @return
         */
        View onBinderViewHolder(int position, View convertView, ViewGroup parent);

        /**
         * 获取当前row item的控件类型
         *
         * @param row
         * @return
         */
        int getItemViewType(int row);

        /**
         * 获取当前控件类型的总数量
         *
         * @return
         */
        int getViewTypeCount();

        /**
         * 获取当前Item的总数量
         *
         * @return
         */
        int getCount();

        /**
         * 获取index Item的高度
         *
         * @return
         */
        int getHeight(int index);


    }
}
